package com.zhixianggou.caibaobox.user.shiro.redis;


import com.zhixianggou.caibaobox.commons.utils.SerializeUtils;
import org.apache.ibatis.cache.CacheException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.springframework.util.CollectionUtils;

import java.util.*;

public class RedisCache<K, V> implements Cache<K, V> {

    private Logger logger = LogManager.getLogger(RedisCache.class);

    private RedisManager cache;

    private String keyPrefix = "shiro_redis_session:";

    public String getKeyPrefix() {
        return this.keyPrefix;
    }

    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    public RedisCache(RedisManager cache) {
        if (cache == null) {
            throw new IllegalArgumentException("Cache argument cannot be null.");
        }
        this.cache = cache;
    }

    public RedisCache(RedisManager cache, String prefix) {
        this(cache);
        this.keyPrefix = prefix;
    }

    private byte[] getByteKey(K key) {
        if (key instanceof String) {
            String preKey = this.keyPrefix + key;
            return preKey.getBytes();
        }
        return SerializeUtils.serialize(key);
    }

    public V get(K key) throws CacheException {
        logger.debug("key [" + key + "]");
        try {
            if (key == null) {
                return null;
            }
            byte[] rawValue = this.cache.get(getByteKey(key));
            V value = (V)SerializeUtils.deserialize(rawValue);
            return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    public V put(K key, V value) throws CacheException {
        logger.debug("putkey [" + key + "]");
        try {
            this.cache.set(getByteKey(key), SerializeUtils.serialize(value));
            return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    public V remove(K key) throws CacheException {
        logger.debug("removekey [" + key + "]");
        try {
            V previous = get(key);
            this.cache.del(getByteKey(key));
            return previous;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    public void clear() throws CacheException {
        logger.debug("redis clear");
        try {
            this.cache.flushDB();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    public int size() {
        try {
            return this.cache.dbSize().intValue();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    public Set<K> keys() {
        try {
            Set<byte[]> keys = this.cache.keys(this.keyPrefix + "*");
            if (CollectionUtils.isEmpty(keys)) {
                return Collections.emptySet();
            }
            Set<K> newKeys = new HashSet<K>();
            for (byte[] key : keys) {
                newKeys.add((K)key);
            }
            return newKeys;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    public Collection<V> values() {
        try {
            Set<byte[]> keys = this.cache.keys(this.keyPrefix + "*");
            if (!CollectionUtils.isEmpty(keys)) {
                List<V> values = new ArrayList<V>(keys.size());
                for (byte[] key : keys) {
                    V value = get((K)key);
                    if (value != null) {
                        values.add(value);
                    }
                }
                return Collections.unmodifiableList(values);
            }
            return Collections.emptyList();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }
}

